package org.example.utils;

import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;

import java.net.URL;
import java.util.Arrays;

/**
 * 人脸识别匹配
 */
public class FaceMatch {
    // 初始化人脸探测器
    static CascadeClassifier faceDetector;
    // 加载OpenCV和引入特征文件
    static {
        try {
            ClassLoader classLoader = FaceMatch.class.getClassLoader();
            // 根据操作系统类型加载对应的本地库文件路径
            String os = System.getProperty("os.name").toLowerCase();

            if (os.contains("win")) {// 在Windows下加载本地库文件的方式
                URL url = classLoader.getResource("lib/opencv_java490.dll");
                if (url!= null) {
                    String dll = url.getPath();
                    System.out.println("Windows 启动 OpenCV ");
                    System.load(dll);
                } else {
                    System.out.println("Could not find opencv dll");
                }
                //引入特征文件
                String xml = ("E:\\workspace\\am\\common\\src\\main\\resources\\haarcascade\\haarcascade_frontalface_default.xml");
                faceDetector = new CascadeClassifier(xml);
            } else if (os.contains("linux")) {// 在Linux下加载本地库文件的方式
                System.out.println("Linux 启动 OpenCV");
                System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
                String xml = ("/home/xiaobiti/workspace/am/common/src/main/resources/haarcascade/haarcascade_frontalface_default.xml");
                faceDetector = new CascadeClassifier(xml);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        switch (get_face("/home/xiaobiti/workspace/am/common/src/main/resources/images/2.jpg")){
            case -1:
                System.out.println("图片文件不存在！");
                break;
            case 0:
                System.out.println("识别到 0 个人脸，请重新识别");
                break;
            case 1:
                System.out.println("识别到 1 个人脸");
                String basePicPath = "/home/xiaobiti/workspace/am/common/src/main/resources/images/";
                double compareHist = match(basePicPath + "1.jpg", basePicPath + "2.jpg");
                System.out.println("人脸相似度：" + compareHist);
                if (compareHist > 0.7) {
                    System.out.println("人脸匹配");
                } else {
                    System.out.println("人脸不匹配");
                }
                break;
            default:
                System.out.println("识别到多个人脸，请重新识别");
        }

    }

    /**
     * 人脸匹配
     * @param source_face 源人脸
     * @param target_face 目标人脸
     * @return 返回人脸匹配相似度
     */
    public static double match(String source_face, String target_face) {
        Mat mat_1 = conv_Mat(source_face);
        Mat mat_2 = conv_Mat(target_face);
        Mat hist_1 = new Mat();
        Mat hist_2 = new Mat();
        //颜色范围
        MatOfFloat ranges = new MatOfFloat(0f, 256f);
        //直方图大小， 越大匹配越精确 (越慢)
        MatOfInt histSize = new MatOfInt(1000);

        Imgproc.calcHist(Arrays.asList(mat_1), new MatOfInt(0), new Mat(), hist_1, histSize, ranges);
        Imgproc.calcHist(Arrays.asList(mat_2), new MatOfInt(0), new Mat(), hist_2, histSize, ranges);

        // CORREL 相关系数
        double res = Imgproc.compareHist(hist_1, hist_2, Imgproc.CV_COMP_CORREL);
        return res;
    }

    /**
     * 灰度化人脸
     * @param img
     * @return
     */
    public static Mat conv_Mat(String img) {
        Mat image0 = Imgcodecs.imread(img);
        Mat image1 = new Mat();
        // 灰度化
        Imgproc.cvtColor(image0, image1, Imgproc.COLOR_BGR2GRAY);
        // 探测人脸
        MatOfRect faceDetections = new MatOfRect();
        faceDetector.detectMultiScale(image1, faceDetections);
        // rect中人脸图片的范围
        for (Rect rect : faceDetections.toArray()) {
            Mat face = new Mat(image1, rect);
            return face;
        }
        return null;
    }

    /**
     * 图片人脸识别
     * @param img_src 图片地址
     * @return 返回识别人脸数量 -1.文件未找到
     */
    public static int get_face(String img_src) {
        Mat image=Imgcodecs.imread(img_src);
        if(image.empty()){return -1;}
        // 1 特征匹配
        MatOfRect face = new MatOfRect();
        faceDetector.detectMultiScale(image, face);
        // 2 匹配 Rect 矩阵 数组
        Rect[] rects=face.toArray();
        if (rects.length==0){return 0;}
        // 3 识别人脸图片裁剪
        Rect rect = rects[0];
        Imgproc.rectangle(image, new Point(rect.x, rect.y),
                new Point(rect.x + rect.width, rect.y + rect.height),
                new Scalar(0, 255, 0), 3);
        imageCut(img_src, img_src, rect.x, rect.y, rect.width, rect.height);// 进行图片裁剪
        return rects.length;
    }

    /**
     * 裁剪人脸
     * @param imagePath 图片路径
     * @param outFile 保存人脸图片地址
     * @param posX 坐标X
     * @param posY 坐标Y
     * @param width 截图宽度
     * @param height 截图高度
     */
    public static void imageCut(String imagePath, String outFile, int posX, int posY, int width, int height) {
        // 原始图像
        Mat image = Imgcodecs.imread(imagePath);
        // 截取的区域：参数,坐标X,坐标Y,截图宽度,截图高度
        Rect rect = new Rect(posX, posY, width, height);
        Mat sub = image.submat(rect);
        Mat mat = new Mat();
        Size size = new Size(width, height);
        Imgproc.resize(sub, mat, size);// 将人脸进行截图并保存
        Imgcodecs.imwrite(outFile, mat);
        System.out.println(String.format("图片裁切成功，裁切后图片文件为： %s", outFile));
    }
}